package com.espirit.file;
import static com.espirit.util.Constant.FORMATTER;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.Instant;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class DirectoryWatcher implements Runnable {
	
	private static final Logger logger = LoggerFactory.getLogger(LoggerFactory.class);
	
	private static final Logger WATCHER_LOGGER = LoggerFactory.getLogger(DirectoryWatcher.class);

	
	private String root;
	
	private Path rootPath ;
	
	private volatile boolean watching;
	
	
	public DirectoryWatcher(String root){
		this.root = root;
	}
	
	public void startWatcher() throws IOException{
		this.watching = true;
		start();
	}

	private void start() throws IOException{
		FileSystem fileSystem = FileSystems.getDefault();
		final WatchService service = fileSystem.newWatchService();
		rootPath = fileSystem.getPath(this.root);
		rootPath.register(service, ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
		Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>(){
			@Override
			public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
				dir.register(service, ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
				return FileVisitResult.CONTINUE;
			}
		});
		
		while(watching){
			WatchKey key = null;
			try {
				key = service.take();
				handleWatchEvent(key);
			} catch (IOException|InterruptedException e) {
				logger.error(e.getMessage(),e);
			}finally{
				if (key!=null) {
					boolean valid = key.reset();
				    if (!valid) {
				        break;
				    }
				}
			}
		}
	}
	
	public void stopWatcher() throws IOException{
		this.watching = false;
	}
	
	private void handleWatchEvent(WatchKey key) throws IOException {
		for (WatchEvent<?> event : key.pollEvents()) {
			WatchEvent.Kind<?> kind = event.kind();
			if (kind == OVERFLOW) {
				return;
			}
			@SuppressWarnings("unchecked")
			WatchEvent<Path> ev = (WatchEvent<Path>)event;
		    Path filePath = ev.context();
		    Path fullPath = filePath.toAbsolutePath();
		    Instant modifyTime = Instant.now();
		    Files.getLastModifiedTime(fullPath);
		    WATCHER_LOGGER.info(fullPath.toString()+"\t" +kind.name()+ "\t" + modifyTime.toEpochMilli() + "\t" + FORMATTER.format(modifyTime) + "\n");
		}
		
	}

	@Override
	public void run() {
		try {
			this.startWatcher();
		} catch (IOException e) {
			logger.error(e.getMessage(),e);
		}
	}
}

